#include "ComponentSwimAroundBehavior.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentCreatureSounds.h"
#include "ComponentPathfinding.h"
#include "Subsystems/SubsystemTerrain.h"
#include "GameRandom.hpp"
#include "Engine/MathUtils.hpp"
#include "Log.h"

#include "Blocks/WaterBlock.h"
#include "Managers/BlocksManager.h"

#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"

#include <iostream>

namespace PocketSurvival
{

    // ************** 【状态机】 **************

    ComponentSwimAroundBehavior::StateMachine::StateMachine(ComponentSwimAroundBehavior *swimAroundBehavior) : m_swimAround(swimAroundBehavior), EasyMachine(MachineState::Inactive) {}

    void ComponentSwimAroundBehavior::StateMachine::enter()
    {
        switch (m_currentState)
        {
        case MachineState::Inactive:
            break;
        case MachineState::Stuck:
            if (GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < 0.5f)
            {
                m_swimAround->m_importanceLevel = 1.0f;
            }
            break;
        case MachineState::Swim:
            m_swimAround->componentPathfinding->stop();
            break;
        }
    }

    void ComponentSwimAroundBehavior::StateMachine::update(float dt)
    {
        switch (m_currentState)
        {
        case MachineState::Inactive:
            MACHINE_DELEGATE()
            {
                if (m_swimAround->isActive())
                {
                    changeTo(MachineState::Swim);
                }
            }
            break;
        case MachineState::Stuck:
            MACHINE_DELEGATE()
            {
                changeTo(MachineState::Swim);
            }
            break;
        case MachineState::Swim:
            MACHINE_DELEGATE()
            {
                if (m_swimAround->componentPathfinding->destination == FastVector3::Zero)
                {
                    Vector3 destination = m_swimAround->findDestination();
                    if (destination != FastVector3::Zero)
                    {
                        m_swimAround->componentPathfinding->setDestination(destination, GameSingleton::gameRandom->getFloat(0.3f, 0.4f), 1.0f, 0, false, true, false, nullptr);
                    }
                    else
                    {
                        m_swimAround->m_importanceLevel = 1.0f;
                    }
                }
                else if (m_swimAround->componentPathfinding->isStuck)
                {
                    changeTo(MachineState::Stuck);
                }
            }
            break;
        }
    }

    void ComponentSwimAroundBehavior::StateMachine::leave() {}

    // **************************************



    ComponentSwimAroundBehavior::~ComponentSwimAroundBehavior()
    {
        delete m_stateMachine;
    }

    float ComponentSwimAroundBehavior::getImportanceLevel()
    {
        return m_importanceLevel;
    }

    Vector3 ComponentSwimAroundBehavior::findDestination()
    {
        BoundingBox boundingBox = componentCreature->componentBody->getBoundingBox();
        Vector3 vector = 0.5f * (boundingBox.Min + boundingBox.Max);
        float num = 2.0f;
        Vector3 result = FastVector3::Zero;
        float num2 = GameSingleton::gameRandom->getFloat(10.0f, 16.0f);
        for (int32_t index = 0; index < 16; index++)
        {
            Vector2 vector2 = GameSingleton::gameRandom->getVector2(1.0f, 1.0f);
            float y = 0.3f + GameSingleton::gameRandom->getFloat(-0.9f, 1.0f);
            Vector3 v = Vector3::Normalize(Vector3(vector2.posX, y, vector2.posY));
            Vector3 vector3 = vector + num2 * v;

            std::shared_ptr<TerrainRaycastResult> terrainRaycastResult = subsystemTerrain->raycast(vector, vector3, false, false, [](int32_t value, float d) ->bool
            {
                int32_t num3 = Terrain::ExtractContents(value);
                return !(GameSingleton::blocksManager->blocks[num3]->isBlock<WaterBlock*>());
            });
            if (terrainRaycastResult == nullptr)
            {
                if (num2 > num)
                {
                    result = vector3;
                    num = num2;
                }
            }
            else if (terrainRaycastResult->distance > num)
            {
                result = vector + v * terrainRaycastResult->distance;
                num = terrainRaycastResult->distance;
            }
        }
        return result;
    }

    void ComponentSwimAroundBehavior::update(float dt)
    {
        if (GameSingleton::gameRandom->getFloat(0.0f, 1.0f) < 0.05f * dt)
        {
            m_importanceLevel = GameSingleton::gameRandom->getFloat(1.0f, 3.0f);
        }
        if (isActive())
        {
            m_stateMachine->update(dt);
        }
        else if (m_stateMachine->currentState() !=  MachineState::Inactive)
        {
            m_stateMachine->changeTo(MachineState::Inactive);
        }
    }

    static const std::string componentName = "SwimAroundBehavior";
    const std::string &ComponentSwimAroundBehavior::getName() const
    {
        return componentName;
    }
    const std::string &ComponentSwimAroundBehavior::GetName()
    {
        return componentName;
    }
    void ComponentSwimAroundBehavior::load(const nlohmann::json &jsonData)
    {
        componentCreature    = GameSingleton::gameRegistry->get<ComponentCreature *>(entity);
        componentPathfinding = GameSingleton::gameRegistry->get<ComponentPathfinding *>(entity);

        subsystemTerrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();

        m_stateMachine = new StateMachine(this);
    }

}
